<template>
  <div class="sz-upload">
    <el-upload
      ref="upload"
      action="#"
      :drag="type === UPLOAD_TYPE.DRAG"
      :list-type="listType"
      :before-upload="beforeUpload"
      :http-request="handleUpload"
      :file-list="fileList"
      :show-file-list="!customFileList"
      :on-remove="handleRemove"
      :on-exceed="handleExceed"
      :on-change="handleSelectChange"
      v-bind="$attrs"
    >
      <slot></slot>
      <template v-if="!$slots.default">
        <template v-if="type === UPLOAD_TYPE.DEFAULT">
          <el-button size="small" type="primary">{{
            btnText || "点击上传"
          }}</el-button>
        </template>
        <template v-if="type === UPLOAD_TYPE.AVATAR">
          <i class="el-icon-plus"></i>
        </template>
        <template v-if="type === UPLOAD_TYPE.DRAG">
          <div slot="trigger">
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">
              将文件拖到此处，或<em>点击上传</em>
            </div>
          </div>
        </template>
        <div class="el-upload__tip" slot="tip">
          {{ tip }}
        </div>
      </template>
    </el-upload>
    <!-- 展示文件列表 -->
    <FileList
      v-if="$attrs['show-file-list'] !== false && customFileList"
      :fileList="fileList"
      :copied="copied"
      @remove="handleRemove"
    />
  </div>
</template>

<script>
import axios from "axios";
import { obsUploader } from "@/utils/upload.js";
import { UPLOAD_TYPE, UPLOAD_STRATEGY } from "@/utils/enum.js";
import FileList from './fileList.vue';
export default {
  name: "SzUpload",
  inheritAttrs: false,
  components: { FileList },
  props: {
    platform: {
      type: String,
      default: "",
      required: true
    },
    tip: {
      type: String,
      default: ""
    },
    type: {
      type: String,
      default: UPLOAD_TYPE.DEFAULT
    },
    btnText: {
      type: String,
      default: "点击上传"
    },
    obsOptions: {
      type: Object,
      default: () => {}
    },
    basePath: {
      type: String,
      default: ""
    },
    customFileName: {
      type: String,
      default: ''
    },
    copied: {
      type: Boolean,
      default: false
    },
    initFiles: {
      type: Array,
      default: () => []
    },
    strategy: {
      // 上传策略：standard（标准--华为）、local（本地部署）
      type: String,
      default: "standard"
    },
    openPartUploadSize: {
      // 超过某个size开启分段上传
      type: Number,
      default: 100 * 1024 * 1024 // 默认100M
    },
    needPartUpload: {
      // 是否开启分段上传
      type: Boolean,
      default: false
    }
  },
  computed: {
    listType() {
      switch (this.type) {
        case UPLOAD_TYPE.DEFAULT:
          return "text";
        case UPLOAD_TYPE.AVATAR:
          return "picture-card";
        case UPLOAD_TYPE.DRAG:
          return "text";
        default:
          return "text";
      }
    },
    isReplace() {
      return this.$attrs.limit === 1 && this.type === UPLOAD_TYPE.AVATAR;
    },
    customFileList() {
      return this.listType === "text";
    }
  },
  data() {
    return {
      obs: {},
      UPLOAD_TYPE,
      curFile: null,
      fileList: []
    };
  },
  watch: {
    initFiles: {
      handler(newVal) {
        if (newVal && newVal.length && !this.fileList.length) {
          this.fileList = JSON.parse(JSON.stringify(newVal));
          this.fileList = this.fileList.map((f) => {
            if (!f.status) {
              f.status = "success";
            }
            return f;
          });
          this.$emit("getFileList", this.fileList);
        }
      },
      immediate: true,
      deep: true
    }
  },
  methods: {
    uploadByObs(file) {
      const obs = obsUploader(this.platform, this.obsOptions);
      this.obs = obs;
      (this.needPartUpload && file.size > this.openPartUploadSize) ? obs.startPartUpload(file) : obs.startUpload(file);
      obs.onSuccess = (res) => {
        this.fileList = this.fileList.map((f) => {
          if (f.uid === file.uid) {
            f.percentage = 100;
            f.status = "success";
            f.url = f.url || `${f.obsHost}/${f.key}`;
          }
          return f;
        });
        this.$emit("getFile", file);
        this.$emit("getFileList", this.fileList);
        this.curFile = null;
      };
      obs.onError = (file, error) => {
        if (!this.curFile) return;
        this.fileList = this.fileList.map((f) => {
          if (f.uid === this.curFile.uid) {
            f.status = "fail";
            f.error = error ? error.msg : "上传失败";
          }
          return f;
        });
        this.$emit("getFile", this.curFile, error);
        this.$emit("getFileList", this.fileList);
        this.curFile = null;
      };
      obs.onProgress = (file, percent) => {
        this.fileList = this.fileList.map((f) => {
          if (f.uid === file.uid) {
            f.percentage = percent;
            f.status = "uploading";
          }
          return f;
        });
        this.$emit("getFileList", this.fileList);
      };
    },
    uploadByLocal(file) {
      const formData = new FormData();
      if (this.basePath) {
        formData.append("basePath", this.basePath);
      }
      formData.append("file", file);
      axios({
        url: this.$attrs.action,
        headers: this.$attrs.headers,
        method: "post",
        data: formData,
        onUploadProgress: (progressEvent) => {
          if (file.status === "success" || file.status === "fail") return;
          const percent =
            ((progressEvent.loaded / progressEvent.total) * 100) | 0;
          this.fileList = this.fileList.map((f) => {
            if (f.uid === file.uid) {
              f.percentage = percent;
              f.status = "uploading";
            }
            return f;
          });
          this.$emit("getFileList", this.fileList);
        }
      })
        .then((res) => {
          this.fileList = this.fileList.map((f) => {
            if (f.uid === file.uid) {
              f.percentage = 100;
              f.status = "success";
              // 兼容实践教学接口result返回的是数组的情况
              if (res.data.result) {
                f.url = Array.isArray(res.data.result) ? res.data.result[0].filePath : res.data.result.filePath;
              } else {
                f.url = "";
              }
            }
            return f;
          });
          this.$emit("getFile", file);
          this.$emit("getFileList", this.fileList);
        })
        .catch(({ response }) => {
          this.fileList = this.fileList.map((f) => {
            if (f.uid === file.uid) {
              f.status = "fail";
              f.error = response ? response.data.message : "上传失败";
            }
            return f;
          });
          this.$emit("getFile", file, response ? response.data : {});
          this.$emit("getFileList", this.fileList);
        });
    },
    beforeUpload(file) {
      let checkRes = true;
      // 暴露文件校验方法
      this.$emit("checkFile", file, (res) => (checkRes = res));
      if (!checkRes) return false;

      // 覆盖默认的存储路径
      if (this.basePath) {
        const suffix = file.name
          ? file.name.substring(file.name.lastIndexOf("."))
          : "";
        const timestamp = Date.now() + Math.round(Math.random() * 10000);
        const fileName = this.customFileName || timestamp + suffix;
        file.key = this.basePath + fileName;
      }
      file.status = "ready";
      file.percentage = 0;
      this.curFile = file;

      // 单个图片直接覆盖
      if (this.isReplace) {
        this.fileList[0] = file;
      } else {
        this.fileList.push(file);
      }
      return true;
    },
    handleUpload({ file }) {
      const { strategy } = this._props;
      if (strategy === UPLOAD_STRATEGY.LOCAL) {
        this.uploadByLocal(file);
        return;
      }
      this.uploadByObs(file);
    },
    handleRemove(file) {
      // 分段上传的文件，上传中被删除则取消上传
      if (file.uploadId && file.status === "uploading") {
        this.obs.abortMultipartUpload(file.key, file.uploadId);
      }
      this.fileList = this.fileList.filter((f) => f.uid !== file.uid);
      this.$emit("removeFile", file, this.fileList);
      this.$emit("getFileList", this.fileList);
    },
    handleExceed(files) {
      if (this.isReplace) {
        // this.$refs.upload.clearFiles();
        const checkPass = this.beforeUpload(files[0]);
        if (checkPass) this.handleUpload({ file: files[0] });
      } else {
        this.$message({
          message: `已达到上传数量上限！`,
          type: "warning"
        });
      }
    },
    handleSelectChange({ raw }) {
      if (this.$attrs["auto-upload"] === false) {
        this.beforeUpload(raw);
        this.$emit("getFileList", this.fileList);
      }
    }
  }
};
</script>

<style lang="scss">
@import url("/styles/upload.scss");
</style>
